﻿using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEditorInternal;
using NinjaToolbox.SubComponentModule.Internal;

namespace NinjaToolbox.SubComponentModule
{
    public sealed class SubComponentContainerGUIDrawer
    {
        string mTitle;

        int mListenSubComponentHash;

        Type[] mSubComponentModelCacheTypeArray;
        Type[] mSubComponentModelEditorCacheTypeArray;

        GenericMenu mCacheGenericMenu;
        List<KeyValuePair<SubComponentInfo, SubComponentModelEditorBase>> mSubComponentModelEditorList;

        List<SubComponentInfo> mSubComponentList;
        List<SubComponentModelBase> mSubComponentModelList;

        string mFilterKeyword;

        ReorderableList mReorderableList;
        ReorderableList ReorderableList
        {
            get
            {
                if (mReorderableList == null)
                {
                    mReorderableList = new ReorderableList(mSubComponentList, typeof(SubComponentInfo));

                    mReorderableList.drawHeaderCallback = (Rect rect) => GUI.Label(rect, mTitle);

                    mReorderableList.onReorderCallbackWithDetails = (ReorderableList list, int oldIndex, int newIndex) =>
                    {
                        var tempModelList = new List<SubComponentModelBase>();

                        foreach (var item in mSubComponentList)
                        {
                            var targetModel = mSubComponentModelList.Find(m => GetSubComponentModelType(item.SubComponent) == m.GetType());
                            tempModelList.Add(targetModel);
                        }

                        mSubComponentModelList.Clear();
                        mSubComponentModelList.AddRange(tempModelList);
                    };

                    mReorderableList.elementHeightCallback = (int index) =>
                    {
                        var subComponentInfo = mSubComponentList[index];

                        var height = 24f;
                        var keyValuePair = mSubComponentModelEditorList.Find(m => m.Key.SubComponent == subComponentInfo.SubComponent);

                        if (keyValuePair.Value != null)
                        {
                            height = keyValuePair.Value.GetFinalHeight();
                        }

                        return height;
                    };

                    mReorderableList.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) =>
                    {
                        var subComponentInfo = mSubComponentList[index];

                        var keyValuePair = mSubComponentModelEditorList.Find(m => m.Key.SubComponent == subComponentInfo.SubComponent);

                        rect.y += 1;

                        if (keyValuePair.Value != null)
                        {
                            keyValuePair.Value.OnGUI(rect);
                        }
                        else
                        {
                            mSubComponentModelEditorList.Clear();
                            CheckSubComponentModelEditor();
                        }
                    };
                }

                mReorderableList.onAddCallback = (list) =>
                {
                    CheckCacheTypeArrayIsCreated();

                    mCacheGenericMenu.ShowAsContext();
                };

                mReorderableList.onRemoveCallback = (list) =>
                {
                    var subComponentInfo = mSubComponentList[list.index];

                    mSubComponentModelList.Remove(subComponentInfo.SubComponent.GetModel());
                    mSubComponentList.RemoveAt(list.index);

                    GUI.changed = true;

                    CheckSubComponentModelEditor();
                };

                return mReorderableList;
            }
        }


        public SubComponentContainerGUIDrawer(SubComponentContainer container, string title = "")
        {
            mTitle = title;

            mSubComponentModelEditorCacheTypeArray = SubComponentUtil.GetChildrenClasses<SubComponentModelEditorBase>();
            mSubComponentModelCacheTypeArray = SubComponentUtil.GetChildrenClasses<SubComponentModelBase>();

            mSubComponentModelEditorList = new List<KeyValuePair<SubComponentInfo, SubComponentModelEditorBase>>();

            mSubComponentList = container.GetType()
                .GetField("mSubComponentList", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.FlattenHierarchy | System.Reflection.BindingFlags.Instance)
                .GetValue(container) as List<SubComponentInfo>;

            mSubComponentModelList = container.GetType()
                .GetField("mSubComponentModelList", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.FlattenHierarchy | System.Reflection.BindingFlags.Instance)
                .GetValue(container) as List<SubComponentModelBase>;
        }

        public void DrawGUI()
        {
            CheckSubComponentModelEditor();

            ReorderableList.DoLayoutList();
        }

        public void ChangeFilterKeyword(string keyword)
        {
            mFilterKeyword = keyword;
        }

        void CheckCacheTypeArrayIsCreated()
        {
            mCacheGenericMenu = new GenericMenu();

            for (int i = 0; i < mSubComponentModelCacheTypeArray.Length; i++)
            {
                var modelType = mSubComponentModelCacheTypeArray[i];

                var finalPathString = "";

                var ignoreAttribute = SubComponentUtil.GetCustomAttribute<IgnoreAttribute>(modelType);

                if (ignoreAttribute != null) continue;

                var filterKeywordAttribute = SubComponentUtil.GetCustomAttribute<FilterKeywordAttribute>(modelType);
                if (!string.IsNullOrEmpty(mFilterKeyword))
                {
                    if (filterKeywordAttribute == null) continue;
                    if (mFilterKeyword != filterKeywordAttribute.Keyword) continue;
                }
                else if (filterKeywordAttribute != null && !string.IsNullOrEmpty(filterKeywordAttribute.Keyword))
                {
                    continue;
                }

                var pathAttributes = SubComponentUtil.GetCustomAttribute<SubComponentPathAttribute>(modelType);

                if (pathAttributes != null)
                    finalPathString = pathAttributes.Path;
                else
                    finalPathString = modelType.Name;

                mCacheGenericMenu.AddItem(new GUIContent(finalPathString), false, () =>
                {
                    var subComponent = SubComponentFactory.CreateSubComponent(modelType);

                    var disallowMultipleSubComponentAttribute = SubComponentUtil.GetCustomAttribute<DisallowMultipleSubComponentAttribute>(modelType);

                    if (disallowMultipleSubComponentAttribute != null)
                    {
                        var targetModel = mSubComponentModelList.Find(m => m.GetType() == modelType);
                        if (targetModel != null)
                        {
                            EditorUtility.DisplayDialog("Error", "The SubComponent can`t be added because already contains the same SubComponent.", "ok");

                            return;
                        }
                    }

                    var requireSubComponentAttribute = SubComponentUtil.GetCustomAttribute<RequireSubComponentAttribute>(modelType);

                    if (requireSubComponentAttribute != null)
                    {
                        var requireType = mSubComponentModelList.Find(m => m.GetType() == requireSubComponentAttribute.ReqireType);
                        if (requireType == null)
                        {
                            EditorUtility.DisplayDialog("Error", "Cant` add SubComponent because not found depends SubComponent.", "ok");

                            return;
                        }
                    }

                    mSubComponentModelList.Add(subComponent.GetModel());
                    mSubComponentList.Add(new SubComponentInfo() { SubComponent = subComponent, SubComponentCachedInterface = subComponent as ISubComponent });

                    CheckSubComponentModelEditor();
                });
            }
        }

        void CheckSubComponentModelEditor()
        {
            var flag = false;

            flag |= mSubComponentList.Count > 0 && mSubComponentList[0].SubComponent.GetHashCode() != mListenSubComponentHash;
            flag |= mSubComponentModelEditorList.Count != mSubComponentList.Count;

            if (flag)
            {
                if (mSubComponentList.Count > 0)
                    mListenSubComponentHash = mSubComponentList[0].SubComponent.GetHashCode();

                mSubComponentModelEditorList.Clear();

                for (int i = 0, iMax = mSubComponentList.Count; i < iMax; i++)
                {
                    var item = mSubComponentList[i];

                    for (int j = 0, jMax = mSubComponentModelEditorCacheTypeArray.Length; j < jMax; j++)
                    {
                        var subComponentModelEditorType = mSubComponentModelEditorCacheTypeArray[j];
                        var customAttribute = SubComponentUtil.GetCustomAttribute<TargetModelAttribute>(subComponentModelEditorType);

                        if (customAttribute != null)
                        {
                            var targetType = customAttribute.Type;
                            var model = item.SubComponent.GetModel();
                            var modelType = model.GetType();

                            if (modelType == targetType)
                            {
                                var renameAttributes = SubComponentUtil.GetCustomAttribute<RenameAttribute>(modelType);
                                var finalName = "";

                                if (renameAttributes != null)
                                    finalName = renameAttributes.NewName;
                                else
                                    finalName = item.SubComponent.GetType().Name;

                                var modelEditor = Activator.CreateInstance(subComponentModelEditorType) as SubComponentModelEditorBase;
                                modelEditor.Initialization(finalName, model);
                                var kvPair = new KeyValuePair<SubComponentInfo, SubComponentModelEditorBase>(item, modelEditor);
                                mSubComponentModelEditorList.Add(kvPair);
                            }
                        }
                    }
                }
            }
        }

        Type GetSubComponentModelType(SubComponentBase subComponent)
        {
            var subComponentType = subComponent.GetType();

            for (int i = 0, iMax = mSubComponentModelCacheTypeArray.Length; i < iMax; i++)
            {
                var item = mSubComponentModelCacheTypeArray[i];

                var targetSubComponentAttribute = SubComponentUtil.GetCustomAttribute<TargetSubComponentAttribute>(item);

                if (targetSubComponentAttribute != null)
                {
                    if (targetSubComponentAttribute.Type == subComponentType)
                        return item;
                }
            }

            return null;
        }
    }
}
